#include "mtools.h"

#define MTOOL_DEBUG 1
#if MTOOL_DEBUG
#define m_pr(fmt, ...) printk(KERN_ALERT "MTOOL PRINT "pr_fmt(fmt), ##__VA_ARGS__)
#else
#define m_pr(fmt, ...) ((void)(0))
#endif

//get the memcinfo structure, info fill in mi
void mt_create_info(struct MemcInfo *mi, const char *ip, int port)
{
  unsigned a, b, c, d;
  strcpy(mi->ip, ip);
  mi->port = port;
  sscanf(ip, "%u.%u.%u.%u", &a, &b, &c, &d);
  mi->addr.sin_family = AF_INET;
  mi->addr.sin_port = htons(port);
  mi->addr.sin_addr.s_addr = htonl((((((a<<8)|b)<<8)|c)<<8)|d);
  m_pr("create info %s:%d\n", ip, port);
}

//return bytes received or error
static int ktcp_recv(struct socket *sock, char *buf, int len)
{
  struct msghdr msg;
	struct kvec iov;
	if(sock->sk==NULL) return -EIO;

	iov.iov_base=buf;
	iov.iov_len=len;

	msg.msg_control=NULL;
	msg.msg_controllen=0;
	msg.msg_flags=0;
	msg.msg_name=NULL;
	msg.msg_namelen=0;
	msg.msg_iov=(struct iovec *)&iov;
	msg.msg_iovlen=1;
	
	return kernel_recvmsg(sock, &msg, &iov, 1, iov.iov_len, 0);
}
static int ktcp_send(struct socket *sock,char *buf,int len)
{
	struct msghdr msg;
	struct kvec iov;

	iov.iov_base=buf;
	iov.iov_len=len;

	msg.msg_control=NULL;
	msg.msg_controllen=0;
	msg.msg_flags=0;
	msg.msg_iov=(struct iovec *)&iov;
	msg.msg_iovlen=1;
	msg.msg_name=NULL;
	msg.msg_namelen=0;
	return kernel_sendmsg(sock,&msg,&iov, 1, iov.iov_len);
}

//success return 0
int mt_set(struct MemcInfo *mi, const char *key, const char *value, int len)
{
  struct socket *excsock;
  int sockfd, ret, buflen = 0, sendlen = 0;
  char *buf;
  sockfd = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP, &excsock);
  if(sockfd < 0){
    pr_err("ERROR: %s sock create failed\n", __FUNCTION__);
    return -EIO;
  }
  ret = excsock->ops->connect(excsock, (struct sockaddr *)&mi->addr, sizeof(mi->addr), 0);
  if(ret < 0){
    pr_err("ERROR %s sock connect failed\n", __FUNCTION__);
    return -EIO;
  }
  buf = kzalloc(strlen(key) + len + 40, GFP_KERNEL);
  sprintf(buf, "set %s 0 0 %d\r\n", key, len);
  buflen = strlen(buf);
  memcpy(buf + buflen, value, len);
  buflen += len;
  buf[buflen++] = '\r';buf[buflen++] = '\n';
  while(sendlen < buflen){
    int one_send_len = ktcp_send(excsock, buf + sendlen, buflen - sendlen);
    if(one_send_len <= 0) {
      ret = -EIO;
      goto out;
    }
    sendlen += one_send_len;
  }
  ktcp_recv(excsock, buf, 40);
  if(strncmp(buf, "STORED", 6)){
    pr_err("ERROR: recv memcached data wrong, not STORED\n");
    goto out;
  }
  ret = 0;
out:
  kfree(buf);
  if(sockfd)
    sys_close(sockfd);
  return ret;
}
//success return 0, no found return 1
int mt_get(struct MemcInfo *mi, const char *key, char *value, int len)
{
  struct socket *excsock;
  int sockfd, ret, buflen = 0, sendlen = 0, alllen;
  int vallen, flag;
  char *buf, *_buf, *p;
  sockfd = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP, &excsock);
  if(sockfd < 0){
    pr_err("ERROR: %s sock create failed\n", __FUNCTION__);
    return -EIO;
  }
  ret = excsock->ops->connect(excsock, (struct sockaddr *)&mi->addr, sizeof(mi->addr), 0);
  if(ret < 0){
    pr_err("ERROR %s sock connect failed\n", __FUNCTION__);
    return -EIO;
  }
  alllen = strlen(key) + 40 + len;
  buf = kzalloc(alllen, GFP_KERNEL);
  sprintf(buf, "get %s\r\n", key, len);
  buflen = strlen(buf);
  while(sendlen < buflen){
    int one_send_len = ktcp_send(excsock, buf + sendlen, buflen - sendlen);
    if(one_send_len <= 0) {
      ret = -EIO;
      goto out;
    }
    sendlen += one_send_len;
  }
  memset(buf, 0, alllen);
  ktcp_recv(excsock, buf, alllen);
  if(!strncmp(buf, "END", 3)){
    ret = 1;
    goto out;
  }else if(strncmp(buf, "VALUE", 5)){
    ret = -EIO;
    goto out;
  }
  _buf = kzalloc(strlen(key) + 1, GFP_KERNEL);
  if(!_buf){
    ret = -ENOMEM;
    goto out;
  }
  sscanf(buf, "VALUE %s %d %d", _buf, &flag, &vallen);
  kfree(_buf);
  if(vallen > len){
    ret = 2;
    goto out;
  }
  p = buf;
  while(*p != '\n')++p;
  ++p;
  memcpy(value, p, vallen);
  ret = 0;
out:
  kfree(buf);
  if(sockfd)
    sys_close(sockfd);
  return ret;
}
